﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BigfootWeb.Helpers
{

    /// <summary>
    /// This class helps us to create javascript code using server side code for commonly performed 
    /// operation. Although I do not like inline javascript, there are some times in the interested
    /// of productivity when it helps to make these items accessbile and repeatable. The funcitons  
    /// in this helper are particularly useful when using in conjunction with the HtmlHelper and
    /// tag builder classes.
    /// 
    /// It abstracts repetive tasks when making ajax calls, validating input, and adding functionality
    /// not currently present in asp.net in an easily repeatable manner.
    /// </summary>
    public class JSHelper
    {

        /// <summary>
        /// Empty constructor
        /// </summary>
        public JSHelper(){}

        /// <summary>
        /// Factory method
        /// </summary>
        public static JSHelper Create()
        {
            return new JSHelper();
        }

        /// <summary>
        /// Outputs a jquery statement to show an element
        /// </summary>
        /// <param name="elementId">The elment id with or without the # selector</param>
        public JQHelper ShowElement(string elementId)
        {
            return JQHelper.Create().SelectID(elementId).show();
        }

        /// <summary>
        /// Outputs a jquery statement to show an element and hide another
        /// </summary>
        /// <param name="showelement">The elment id to show with or without the # selector</param>
        /// <param name="hideelement">The element id to show with or without the # selector</param>
        public JQHelper ShowAndHideElement(string showelement, string hideelement)
        {
            return JQHelper.Create().SelectID(showelement).show().SelectID(hideelement).hide();
        }

        /// <summary>
        /// Outputs a jquery statement to hide an element
        /// </summary>
        /// <param name="elementId">The elment id with or without the # selector</param>
        public JQHelper HideElement(string elementId)
        {
            return JQHelper.Create().SelectID(elementId).hide();
        }

        /// <summary>
        /// Outputs a jquery statement to clear the innerHtml of an element
        /// </summary>
        /// <param name="elementId">The elment id with or without the # selector</param>
        public JQHelper ClearElement(string elementId)
        {
            return JQHelper.Create().SelectID(elementId).clear();
        }

        /// <summary>
        /// Outputs a jquery statement to show an element if it is hidden, or hide it if it is showing
        /// </summary>
        /// <param name="elementId">The elment id with or without the # selector</param>
        public JQHelper ToggleElement(string elementId)
        {
            return JQHelper.Create().SelectID(elementId).toggle();
        }

        /// <summary>
        /// Submits the parent form of the the elment that invokes this script
        /// </summary>
        public JSBuilder Submit()
        {
            return JSBuilder.Create().AddLine("jQuery(this).parents('form:eq(0)').trigger('submit')");
        }

        /// <summary>
        /// Submits the form specified
        /// </summary>
        public JSBuilder Submit(string formName)
        {
            var js = new JSBuilder();
            js.AddLine("jQuery('#" + formName + "').trigger('submit')");
            return js;
        }

        /// <summary>
        /// Submits the parent form of the the elment that invokes this script, and optionally does validation.
        /// When validatInputs is left empty the script and validate=true then the form is validated
        /// </summary>
        public JSBuilder Submit(bool Validate, params string[] validateInputs)
        {
            var js = new JSBuilder();
            js.AddLine("jQuery('form').trigger('submit')");
            if (Validate)
            {
                if (validateInputs != null && validateInputs.Length > 0)
                {
                    js.WrapValidation(validateInputs);
                }
                else
                {
                    js.WrapValidation();
                }
            }
            return js;
        }

        /// <summary>
        /// Submits the first form in the page via ajax to the url specified, and updates the supplied updatePanel with the value
        /// returned from the server. Optionally does validation or not
        /// </summary>
        public JSBuilder SubmitAjax(bool validate, string postUrl, string updatePanel)
        {
            return SubmitAjax(validate, postUrl, updatePanel, null);
        }

        /// <summary>
        /// Submits the first form in the page via ajax to the url specified, and updates the supplied updatePanel with the value
        /// returned from the server. Optionally does validation or not for the supplied validate inputs.
        /// If you are validating the whole form, then you can simply leave the validat inputs empty
        /// </summary>
        public JSBuilder SubmitAjax(bool validate, string postUrl, string updatePanel, params string[] validateInputs)
        {
            //jQuery(formName).ajaxSubmit({target:'#addnote_container', url: '/thcrm/desktopmodules/thcrm/route.aspx?routec=notes&routea=add&entityid=260'});   
            var js = new JSBuilder();
            js.AddLine("jQuery('form:eq(0)').ajaxSubmit({ target: " + JSBuilder.GetString("#" + updatePanel) + ", url: " + JSBuilder.GetString(postUrl) + "})");
            if (validate)
            {
                if (validateInputs != null && validateInputs.Length > 0)
                {
                    js.WrapValidation(validateInputs);
                }
                else
                {
                    js.WrapValidation();
                }
            }
            return js;
        }

        /// <summary>
        /// Sets the focus to the element specified
        /// </summary>
        public string SetFocus(string elementId)
        {
            return JQHelper.Create().SelectID(elementId).AddCommand("focus()").ToScriptBlock();
        }

        //private static string jQuery_OnReady(string statement)
        //{
        //    if (statement.TrimEnd().EndsWith(";") == false)
        //        statement += "; ";

        //    var script = new StringBuilder();
        //    script.Append("<script type='text/javascript'>");
        //    script.Append("jQuery(document).ready(function() { " + statement + "});");
        //    script.Append("</script>");

        //    return script.ToString();
        //}

        /// <summary>
        /// Performs an ajax request and loads the content of the supplied url into the elementid spedcied.
        /// You can also optionally toggle the contents, this means that if the content is showing the
        /// ajax call is not performed and the elementid's content is cleared and hidden. When the element
        /// is hidden then the ajax request happens and the the fills the element and shows it
        /// </summary>
        /// <param name="url">Url to request from</param>
        /// <param name="elementid">The element that will hold the response</param>
        /// <param name="toggle">When true retreives the content and fills the element only when it is not showing</param>
        public JSBuilder Load(string url, string elementid, bool toggle)
        {
            var js = new JSBuilder();
            if (toggle)
            {
                js.Add("if(jQuery('#" + elementid + "').is(':visible')) { jQuery('#" + elementid + "').html('').hide(); } else { jQuery('#" + elementid + "').load('" + JSBuilder.CleanString(url) + "').show(); }");
            }
            else
            {
                js.Add("jQuery('#" + elementid + "').load('" + JSBuilder.CleanString(url) + "').show();");
            }
            return js;
        }

        /// <summary>
        /// Creates a html element into a virtual form, or a partial form. It outputs the correct scripts to wrap validation
        /// and form submission around the inputs inside the supplied virtual form element. By default all elements inside the virtual form
        /// with a class assigned named causesValidation trigger a validation call whenever clicked
        /// i.e. to setup a partial form you call CreatePartialForm("addUser") where addUser is the name of a div
        ///      to then submit the form you simply call SubmitPartialForm("addUser", "/users/createuser") when this happens only the inputs 
        ///      within the div.addUser are validated and submitted via ajax to url specified
        /// </summary>
        /// <param name="formId">The element id that wraps the group of inputs you want to make into a form</param>
        /// <returns>Returns a function named ValidateGroup_{formId} which is called by the SubmitPartialForm method to validate before submitting a form</returns>
        public string CreatePartialForm(string formId)
        {
            if (formId.StartsWith("#"))
                formId = formId.Substring(1);
            var functionName = "ValidateGroup_" + formId;
            var setupFunctionName = "CreatePartialForm_" + formId;
            var setupRetryVariable = setupFunctionName + "_Retry";
            var script = new StringBuilder();
            script.AppendLine("<script type='text/javascript'>");
            script.AppendLine(" var " + setupRetryVariable + " = 0;");
            script.AppendLine(" jQuery(document).ready(function() {");
            script.AppendLine("     setTimeout(" + setupFunctionName + ", 1);");
            script.AppendLine(" });");
            script.AppendLine(" function " + setupFunctionName + "(){");
            script.AppendLine("     if (typeof(jQuery.fn.validate) == 'undefined' && " + setupRetryVariable + " < 5) { bigfootWeb.log('retryValidate'); " + setupRetryVariable + " += 1; setTimeout(" + setupFunctionName + ", 500); }");
            script.AppendLine("     jQuery('#" + formId + "').parents('form:eq(0)').validate({onsubmit: false});");
            script.AppendLine("     jQuery('#" + formId + " .causesValidation').click(" + functionName + ");");
            script.AppendLine("     jQuery('#" + formId + " :text').keydown(function(evt) { if (evt.keyCode == 13) { " + functionName + "(evt); } });");
            script.AppendLine(" }");
            script.AppendLine(" function " + functionName + "(evt) {");
            script.AppendLine("     var isValid = true;");
            script.AppendLine("     jQuery('#" + formId + "').find(':input').each(function(i, item) { if (!jQuery(item).valid()) isValid = false; });");
            script.AppendLine("     if (!isValid && evt) evt.preventDefault();");
            script.AppendLine("     return isValid;");
            script.AppendLine(" }");
            script.AppendLine("</script>");
            return script.ToString();
        }

        /// <summary>
        /// Submits a previously setup partial form through CreatePartialForm method to the url specified
        /// </summary>
        /// <param name="formId">The element id containing the partial form</param>
        /// <param name="url">The url you want to submit the partial form to</param>
        public AjaxRequest SubmitPartialForm(string formId, string url)
        {
            return SubmitPartialForm(formId, url, true);
        }

        /// <summary>
        /// Submits a previously setup partial form through CreatePartialForm method to the url specified
        /// </summary>
        /// <param name="formId">The element id containing the partial form</param>
        /// <param name="url">The url you want to submit the partial form to</param>
        /// <param name="validate">Determines weather to validate before submission or not. By default all elements within
        /// the parial form that have the class causesValidation trigger a validation call when clicked</param>
        public AjaxRequest SubmitPartialForm(string formId, string url, bool validate)
        {
            var ajax = AjaxRequest.Create(url);
            ajax.Post().PartialForm(formId);
            if (validate)
                ajax.Validate();
            return ajax;
        }
    }
}
